from PyQt5.QtCore import Qt, QTimer,pyqtProperty,pyqtSlot,pyqtSignal,QModelIndex
from QuickModelBase import QuickModelBase
# from myglobal import MySet
import logging
roleHighlight=Qt.UserRole+3
# class TAO_API QuickListModel : public QuickModelBase<QuickListItemBase*>
# {
#     Q_OBJECT
#     Q_PROPERTY(bool allChecked READ allChecked WRITE setAllChecked NOTIFY allCheckedChanged)
#     AUTO_PROPERTY(int, visibledCount, 0)
#     AUTO_PROPERTY(int, selectedCount, 0)
#     AUTO_PROPERTY(int, checkedCount, 0)

#     AUTO_PROPERTY(QStringList, headerRoles, {})
#     AUTO_PROPERTY(Qt::SortOrder, sortOrder, Qt::AscendingOrder)
#     AUTO_PROPERTY(QString, sortRole, {})
#     AUTO_PROPERTY(QStringList, noSortRoles, {})
# public:
#     using Super = QuickModelBase<QuickListItemBase*>;
#     explicit QuickListModel(QObject* parent = nullptr);
#     ~QuickListModel() override;
#     Q_INVOKABLE QVariant data(int row) const
#     {
#         return Super::data(row);
#     }
#     //[begin] check
#     bool allChecked() const
#     {
#         return self._allChecked;
#     }
#     Q_INVOKABLE void check(int row, bool checked);
#     //[end] check

#     //[begin] search. control visible
#     Q_INVOKABLE void search(const QString& searchKey);
#     const QString& searchKey() const
#     {
#         return mSearchkey;
#     }
#     Q_INVOKABLE void searchImmediate(const QString& searchKey);
#     Q_INVOKABLE void clearSearchKey()
#     {
#         mSearchkey.clear();
#     }
#     //[end] search

#     using VisibleCallback = std::function<bool(QuickListItemBase*)>;
#     void setVisibleFilter(const VisibleCallback& callback)
#     {
#         mVisibleCallback = callback;
#     }

#     QList<QuickListItemBase*> allCheckedDatas() const;

#     //[begin] select
#     Q_INVOKABLE void deselectAll();
#     Q_INVOKABLE void selectAll();
#     Q_INVOKABLE bool isSelected(int row) const;
#     Q_INVOKABLE void select(int row);
#     Q_INVOKABLE void deselect(int row);
#     Q_INVOKABLE void selectRange(int from, int to);
#     Q_INVOKABLE void selectSingle(int row);
#     //[end] select

#     Q_INVOKABLE void doPress(int row, bool shift, bool ctrl, bool outRange);
#     Q_INVOKABLE void doMove(int row, bool outRange);
#     Q_INVOKABLE void doRelease();

#     //[begin] sort

#     using SortCallback = std::function<bool(QuickListItemBase*, QuickListItemBase*)>;
#     // Map <key, callBack> ,key should match to headerRoles
#     void setSortCallbacksAscend(const QMap<QString, SortCallback>& callbacksMap)
#     {
#         mSortCallbacksAscend = callbacksMap;
#     }

#     Q_INVOKABLE virtual void sortByRole();
#     //[end] sort

#     void updateCalcInfo() override;

#     Q_INVOKABLE void notifyScrollTo(int index)
#     {
#         emit scrollTo(index);
#     }
# public slots:
#     void setAllChecked(bool allChecked);

# signals:
#     void scrollTo(int index);
#     void allCheckedChanged(bool allChecked);

#     void selectedAction();

#     void signalUpdateCalcCount();

#     void beginSearch();
#     void endSearch();
# protected slots:
#     void onSearch();

# protected:
#     void updateAllCheck();
#     void updateVisibleCount();
#     void updateSelectedCount();
#     void updateCheckedCount();
#     void updateAlternate();

# protected:
#     bool self._allChecked = false;
#     bool mIsPressed = false;

#     int mLastPressedRow = -1;

#     QMap<QString, SortCallback> mSortCallbacksAscend;
#     QString mSearchkey;
#     QTimer mSearchTimer;
#     VisibleCallback mVisibleCallback;
# };
class QuickListModel(QuickModelBase):
    scrollTo=pyqtSignal(int);
    signalUpdateCalcCount=pyqtSignal()
    beginSearch=pyqtSignal()
    endSearch=pyqtSignal()
    selectedAction=pyqtSignal()
    def __init__(self,parent=None):
        super().__init__(parent)
        self.mSearchTimer=QTimer()
        self.mSearchTimer.timeout.connect(self.onSearch)
        self.mSearchTimer.setInterval(300);
        self.mSearchTimer.setSingleShot(True);
        self._headerRoles=[]
        self._allChecked=False
        self.mIsPressed=False
        self.mLastPressedRow=-1
        self._visibledCount=0
        self.mSearchkey=""
        self._sortRole=None
        self._visibledCount=0
        self._selectedCount=0
        self.checkedCount=0
    visibleCountChanged=pyqtSignal()
    @pyqtProperty(int, notify=visibleCountChanged)
    def visibledCount(self):
        return self._visibledCount
    @visibledCount.setter
    def visibledCount(self,v):
        self._visibledCount=v
        self.visibleCountChanged.emit()

    selectedCountChanged=pyqtSignal()
    @pyqtProperty(int, notify=selectedCountChanged)
    def selectedCount(self):
        return self._selectedCount
    @selectedCount.setter
    def selectedCount(self,v):
        self._selectedCount=v
        self.selectedCountChanged.emit()
    
    checkedCountChanged=pyqtSignal()
    @pyqtProperty(int, notify=checkedCountChanged)
    def checkedCount(self):
        return self._checkedCount
    @checkedCount.setter
    def checkedCount(self,v):
        self._checkedCount=v
        self.checkedCountChanged.emit()
    
    headerRolesChanged=pyqtSignal()
    @pyqtProperty(list, notify=headerRolesChanged)
    def headerRoles(self):
        return self._headerRoles
    @headerRoles.setter
    def headerRoles(self, value):
        self._headerRoles=value
        self.headerRolesChanged.emit()
    sortRoleChanged=pyqtSignal(bool)
    @pyqtProperty(str, notify=sortRoleChanged)
    def sortRole(self):
        return self._sortRole
    @sortRole.setter
    def sortRole(self, value):
        self._sortRole=value

    allCheckedChanged=pyqtSignal(bool)
    @pyqtProperty(bool, notify=allCheckedChanged)
    def allChecked(self):
        return self._allChecked
    @allChecked.setter
    def allChecked(self, value):
        self._allChecked=value

    @pyqtSlot(int,bool)
    def check(self, row, checked):
        if (row < 0 or row >= len(self.mDatas)):
            return;
        if self.mDatas[row]["isChecked"]==checked:
            return
        else:
            self.mDatas[row]["isChecked"]=checked
            self.dataChanged.emit(self.index(row, 0), self.index(row,0), [Qt.EditRole,Qt.DisplayRole]);
            # if (self.mDatas[row]["isSelected"]):
            #     for obj in self.mDatas:
            #         if obj["isSelected"]:
            #             obj["isChecked"]=checked

            allCheck = True;
            if (checked == False or len(self.mDatas) == 0):
                allCheck = False;
            else:
                for obj in  self.mDatas:
                    if obj["isChecked"]==False:
                        allCheck = False;
                        break;
            if (self._allChecked != allCheck):
                self._allChecked = allCheck;
                self.allCheckedChanged.emit(self._allChecked);
            self.updateCheckedCount()
    @pyqtSlot(bool)
    def setAllChecked(self, allChecked):
        if (self._allChecked == allChecked):
            logging.info("return")
            return;
        logging.info("check all")
        for obj in self.mDatas:
            obj["isChecked"]=allChecked
        self.updateCheckedCount()
        self._allChecked = allChecked;
        self.allCheckedChanged.emit(self._allChecked);
        logging.info("emit allchecked")
        self.layoutChanged.emit();
        # self.dataChanged.emit(self.index(0, 0), self.index(len(self.mDatas),0), [Qt.EditRole,Qt.DisplayRole]);
    def search(self, searchKey):
        mSearchkey = searchKey.simplified();
        # // mSearchTimer.start(400);
        onSearch();
    def searchImmediate(self, searchKey):
        mSearchkey = searchKey.simplified();
        onSearch();
    def onSearch(self):
        self.beginSearch.emit();

        newDatas=[]

        vcount = 0;
        for obj in  self.mAllDatas:
            v = mVisibleCallback is None or mVisibleCallback(obj);
            m = obj.match(mSearchkey);

            if (v and m):
                newDatas.push_back(obj);
                vcount+=1

        if (not compareDataChanged(self.mDatas, newDatas)):
            sortByRole();
            self.endSearch.emit();
            return;

        self.mDatas = newDatas;
        sortByRole();
        beginResetModel();
        endResetModel();
        updateCalcInfo();
        self.endSearch.emit();
    @pyqtSlot()
    def deselectAll(self):
        for obj in self.mDatas:
            obj["isSelected"]=False
        self.updateSelectedCount();
        self.selectedAction.emit();
        self.layoutChanged.emit();
        # self.dataChanged.emit(self.index(0, 0), self.index(len(self.mDatas),0), [Qt.EditRole,Qt.DisplayRole]);
    @pyqtSlot()
    def selectAll(self):
        for obj in self.mDatas:
            obj["isSelected"]=True
        self.updateSelectedCount();
        self.selectedAction.emit();
        self.layoutChanged.emit();
        # self.dataChanged.emit(self.index(0, 0), self.index(len(self.mDatas),0), [Qt.EditRole,Qt.DisplayRole]);
    @pyqtSlot(int)
    def isSelected(self, row):
        if (row < 0 or row >= len(self.mDatas)):
            return False;
        return self.mDatas[row]["isSelected"]

    def select(self,row):
        if (row < 0 or row >= len(self.mDatas)):
            return;
        self.mDatas[row]["isSelected"]=True
        self.updateSelectedCount();
        self.selectedAction.emit();
        self.dataChanged.emit(self.index(row, 0), self.index(row,0), [Qt.EditRole,Qt.DisplayRole]);
    def deselect(self,row):
        if (row < 0 or row >= len(self.mDatas)):
            return;
        self.mDatas[row]["isSelected"]=False
        self.updateSelectedCount();
        self.selectedAction.emit();
        self.dataChanged.emit(self.index(row, 0), self.index(row,0), [Qt.EditRole,Qt.DisplayRole]);

    def selectRange(self, from1, to):
        minRow = min(from1, to);
        maxRow = max(from1, to);
        i=0
        while i < len(self.mDatas):
            self.mDatas[i]["isSelected"]=(minRow <= i and i <= maxRow);
            i+=1
        self.updateSelectedCount();
        self.selectedAction.emit();
        self.dataChanged.emit(self.index(minRow, 0), self.index(maxRow,0), [Qt.EditRole,Qt.DisplayRole]);
    def selectSingle(self, row):
        if row < 0 or row >= len(self.mDatas):
            return
        minRow=len(self.mDatas)
        maxRow=-1
        for i in range(len(self.mDatas)):
            o=self.mDatas[i]
            if i==row:
                if o["isSelected"]==True:
                    pass
                else:
                    o["isSelected"]=True
                    if minRow>i:
                        minRow=i
                    if maxRow<i:
                        maxRow=i

            else:
                if o["isSelected"]==False:
                    pass
                else:
                    o["isSelected"]=False
                    if minRow>i:
                        minRow=i
                    if maxRow<i:
                        maxRow=i
        self.updateSelectedCount();
        self.selectedAction.emit()
        self.dataChanged.emit(self.index(minRow, 0), self.index(maxRow,0), [Qt.EditRole,Qt.DisplayRole]);

    @pyqtSlot(int,bool,bool,bool)
    def doPress(self,row, shift, ctrl, outRange):
        logging.info([row,shift,ctrl,outRange])
        if (outRange):
            row =len(self.mDatas)
        else:
            if row < 0 or row >= len(self.mDatas):
                return;

        self.mIsPressed = True;
        if (ctrl):
            self.mLastPressedRow = row;
            if (self.isSelected(self.mLastPressedRow)):
                self.deselect(self.mLastPressedRow);
            else:
                self.select(self.mLastPressedRow);
        elif (shift):
            self.selectRange(self.mLastPressedRow, row);
        else:
            self.mLastPressedRow = row;
            self.selectSingle(self.mLastPressedRow);
    @pyqtSlot(int,bool)
    def doMove(self,row, outRange):
        if (outRange):
            row = len(self.mDatas)
        else:
            if row < 0 or row >= len(self.mDatas):
                return;
        if (self.mIsPressed):
            self.selectRange(self.mLastPressedRow, row);
    @pyqtSlot()
    def doRelease(self):
        self.mIsPressed = False;
    @pyqtSlot()
    def sortByRole(self):
        return #todo
        if (self.mDatas.size() <= 1):
            return;
        sortCall == mSortCallbacksAscend.value(sortRole())
        if sortCall:
            copyObjs=[]
            copyObjs = self.mDatas;
            if (self.sortOrder() == Qt.SortOrder.AscendingOrder):
                pass #std.sort(copyObjs.begin(), copyObjs.end(), sortCall);
            else:
                # swap param position
                pass #std.sort(copyObjs.begin(), copyObjs.end(), lambda  a, b: sortCall(b, a))

            self.mDatas = copyObjs;
            self.dataChanged.emit(index(0, 0), index(self.mDatas.count() - 1, 0));
            self.updateAlternate();

    def allCheckedDatas(self):
        dataList=[]
        for data in self.mAllDatas:
            if (data.isChecked()):
                dataList.push_back(data);
        return dataList;

    def updateAllCheck(self):
        allCheck = False;
        if (not len(self.mDatas)==0):
            allCheck=False# = std.all_of(self.mDatas.begin(), self.mDatas.end(), lambda obj: obj.isChecked());
        if (self._allChecked == allCheck):
            return;

        self._allChecked = allCheck;
        self.allCheckedChanged.emit(self._allChecked);

    def updateVisibleCount(self):
        count = len(self.mDatas)
        self.visibledCount=count

    def updateSelectedCount(self):
        count = 0 #std.count_if(self.mDatas.begin(), self.mDatas.end(), lambda obj : obj.isChecked())
        for  one in self.mDatas:
            if one["isSelected"]:
                count+=1
        self.selectedCount=count

    def updateCheckedCount(self):
        count = 0 #std.count_if(self.mDatas.begin(), self.mDatas.end(), lambda obj : obj.isChecked())
        for  one in self.mDatas:
            logging.info(["one",one])
            if one["isChecked"]:
                count+=1
        self.checkedCount=count
    def updateAlternate(self):
        alter = False;
        for  obj in self.mDatas:
            # obj._isAlternate=alter#.set_isAlternate(alter);
            obj["isAlternate"]=alter
            alter = not alter;
    def updateCalcInfo(self):
        self.updateAllCheck();
        self.updateCheckedCount();
        self.updateVisibleCount();
        self.updateSelectedCount();
        self.updateAlternate();
        self.signalUpdateCalcCount.emit();
    def setSortCallbacksAscend(self, callbacksMap):
        self.mSortCallbacksAscend = callbacksMap
if __name__=="__main__":
    m=QuickListModel()
    m.init_data(MySet([{"name":1},{"name":2},{"name":3}]))
    print(m)
    print(m.rowCount())
    m.append(MySet([{"name":4},{"name":5},{"name":6}]))
    print("m",m)
    m.prepend({"name":7})
    print(m)
    # print("/////////////////////")
    m.insert_many(0, MySet([{"name":9}, {"name":10}]))
    print(m)